package org.geeks.gscaffeine.func;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

@Slf4j
public class CaffeineEvictionT {

  @Test
  public void sizeBased() {
    // Evict based on the number of entries in the cache
    Cache<String, String> cache0 = Caffeine.newBuilder().maximumSize(4).build();

    IntStream.range(0, 7)
        .forEach(
            i -> {
              cache0.put("K-" + i, "V-" + i);
              cache0.cleanUp();
              log.info("Cache-0` size is {}, value is {}", cache0.estimatedSize(), cache0.asMap());
            });

    log.info("-------------------------------------------------");

    cache0
        .policy()
        .eviction()
        .ifPresent(
            eviction -> {
              eviction.setMaximum(2 * eviction.getMaximum());
            });

    IntStream.range(0, 7)
        .forEach(
            i -> {
              cache0.put("K-" + i, "V-" + i);
              cache0.cleanUp();
              log.info("Cache-0` size is {}, value is {}", cache0.estimatedSize(), cache0.asMap());
            });

    log.info("-------------------------------------------------");

    // Evict based on the number of vertices in the cache
    Cache<String, String> cache1 =
        Caffeine.newBuilder().maximumWeight(4).weigher((String key, String value) -> 1).build();

    IntStream.range(0, 7)
        .forEach(
            i -> {
              cache1.put("K-" + i, "V-" + i);
              log.info("Cache-1 is {}", cache1.asMap());
            });
  }

  @Test
  public void timeBased() throws Exception {
    // Evict based on a fixed expiration policy
    Cache<String, String> cache0 =
        Caffeine.newBuilder().expireAfterAccess(2, TimeUnit.SECONDS).build();

    cache0.put("K-0", "V-0");

    log.info("K-0` value is {}", cache0.getIfPresent("K-0"));

    Thread.sleep(1000);

    log.info("K-0` value is {}", cache0.getIfPresent("K-0"));

    Thread.sleep(1000);

    log.info("K-0` value is {}", cache0.getIfPresent("K-0"));

    log.info("-------------------------------------------------");

    Cache<String, String> cache1 =
        Caffeine.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build();

    cache1.put("K-0", "V-0");

    log.info("K-0` value is {}", cache1.getIfPresent("K-0"));

    Thread.sleep(1000);

    log.info("K-0` value is {}", cache1.getIfPresent("K-0"));

    Thread.sleep(1000);

    log.info("K-0` value is {}", cache1.getIfPresent("K-0"));

    log.info("-------------------------------------------------");

    // Evict based on a varying expiration policy
    Cache<String, String> cache2 =
        Caffeine.newBuilder()
            .expireAfter(
                new Expiry<String, String>() {
                  public long expireAfterCreate(String key, String graph, long currentTime) {
                    return currentTime;
                  }

                  public long expireAfterUpdate(
                      String key, String graph, long currentTime, long currentDuration) {
                    return currentDuration;
                  }

                  public long expireAfterRead(
                      String key, String graph, long currentTime, long currentDuration) {
                    return currentDuration;
                  }
                })
            .build();
  }

  @Test
  public void referenceBased() {
    // Evict when neither the key nor value are strongly reachable
    Cache<String, String> cache0 = Caffeine.newBuilder().weakKeys().weakValues().build();

    IntStream.range(0, 7)
        .forEach(
            i -> {
              cache0.put("K-" + i, "V-" + i);
              log.info("Cache-0 is {}", cache0.asMap());
            });

    log.info("-------------------------------------------------");

    // Evict when neither the key nor value are strongly reachable
    Cache<String, String> cache1 = Caffeine.newBuilder().softValues().build();

    IntStream.range(0, 7)
        .forEach(
            i -> {
              cache1.put("K-" + i, "V-" + i);
              log.info("Cache-0 is {}", cache1.asMap());
            });
  }
}
